//
//  TableModel.swift
//  Risac
//
//  Created by remy on 2018/3/22.
//

import UIKit

public protocol ListModel: AnyObject {
    func cellItemsCount(at section: Int?) -> Int
    func removeAll()
}

/// UITableView 的 dataSource 代理对象
public final class TableModel: NSObject, ListModel {
    
    public var sectionItems: [TableSectionItem] = []
    /// section索引-值列表
    public var sectionIndexTitles: [String]?
    /// section索引-值列表对应的section
    public var sectionForSectionIndexTitle: [Int]?
    
    public func set(_ cellItem: TableCellItem) {
        set([cellItem])
    }
    
    public func set(_ cellItems: [TableCellItem]) {
        guard cellItems.count > 0 else { return }
        if sectionItems.count == 0 {
            sectionItems.append(TableSectionItem())
        }
        sectionItems.last!.cellItems = cellItems
    }
    
    public func set(_ sectionItem: TableSectionItem) {
        set([sectionItem])
    }
    
    public func set(_ sectionItems: [TableSectionItem]) {
        guard sectionItems.count > 0 else { return }
        self.sectionItems = sectionItems
    }
    
    public func set(_ cellItem: TableCellItem, at indexPath: IndexPath) {
        guard let sectionItem = sectionItems[safe: indexPath.section] else { return }
        sectionItem.cellItems.safeSet(cellItem, at: indexPath.row)
    }
    
    public func set(_ sectionItem: TableSectionItem, at section: Int) {
        sectionItems.safeSet(sectionItem, at: section)
    }
    
    public func add(_ cellItem: TableCellItem, at section: Int? = nil) {
        add([cellItem], at: section)
    }
    
    public func add(_ cellItems: [TableCellItem], at section: Int? = nil) {
        guard cellItems.count > 0 else { return }
        if let section = section {
            sectionItems[safe: section]?.cellItems.append(contentsOf: cellItems)
        } else {
            // 不指定section则默认添加到最后的section内
            if sectionItems.count == 0 {
                sectionItems.append(TableSectionItem())
            }
            sectionItems.last!.cellItems.append(contentsOf: cellItems)
        }
    }
    
    public func add(_ sectionItem: TableSectionItem) {
        add([sectionItem])
    }
    
    public func add(_ sectionItems: [TableSectionItem]) {
        guard sectionItems.count > 0 else { return }
        self.sectionItems.append(contentsOf: sectionItems)
    }
    
    public func insert(_ cellItem: TableCellItem, at indexPath: IndexPath) {
        return insert([cellItem], at: indexPath)
    }
    
    public func insert(_ cellItems: [TableCellItem], at indexPath: IndexPath) {
        guard cellItems.count > 0 else { return }
        sectionItems[safe: indexPath.section]?.cellItems.safeInsert(cellItems, at: indexPath.row)
    }
    
    public func insert(_ sectionItem: TableSectionItem, at section: Int) {
        insert([sectionItem], at: section)
    }
    
    public func insert(_ sectionItems: [TableSectionItem], at section: Int) {
        guard sectionItems.count > 0 else { return }
        self.sectionItems.safeInsert(sectionItems, at: section)
    }
    
    @discardableResult
    public func remove(at section: Int) -> Bool {
        sectionItems.safeRemove(at: section)
    }
    
    @discardableResult
    public func remove(at indexPath: IndexPath) -> Bool {
        if let sectionItem = sectionItems[safe: indexPath.section] {
            return sectionItem.cellItems.safeRemove(at: indexPath.row)
        }
        return false
    }
    
    public func removeAll() {
        sectionItems = []
    }
    
    public func cellItems(at section: Int? = nil) -> [TableCellItem] {
        if let section = section {
            return sectionItems[safe: section]?.cellItems ?? []
        }
        return sectionItems.reduce(into: [TableCellItem]()) {
            $0.append(contentsOf: $1.cellItems)
        }
    }
    
    public func cellItemsCount(at section: Int? = nil) -> Int {
        if let section = section {
            return sectionItems[safe: section]?.cellItems.count ?? 0
        }
        return sectionItems.reduce(0) { $0 + $1.cellItems.count }
    }
    
    public func cellItem(at indexPath: IndexPath) -> TableCellItem? {
        return sectionItems[safe: indexPath.section]?.cellItems[safe: indexPath.row]
    }
}

extension TableModel {
    
    private func cellView(with cellClass: TableCell.Type, tableView: UITableView, cellItem: TableCellItem) -> UITableViewCell {
        let identifier = cellClass.metaTypeName + cellItem.cellReuseIDSuffix
        var cell = tableView.dequeueReusableCell(withIdentifier: identifier)
        if cell == nil {
            cell = (cellClass as UITableViewCell.Type).init(style: .default, reuseIdentifier: identifier)
        }
        (cell as! TableCell).updateBinding(cellItem: cellItem)
        return cell!
    }
}

extension TableModel: UITableViewDataSource {
    
    public func numberOfSections(in tableView: UITableView) -> Int {
        return sectionItems.count
    }
    
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let item = sectionItems[safe: section], item.showCellItems {
            return item.cellItems.count
        }
        return 0
    }
    
    public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sectionItems[safe: section]?.sectionHeaderTitle
    }
    
    public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
        return sectionItems[safe: section]?.sectionFooterTitle
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let item = cellItem(at: indexPath) {
            if let cls = item.cellClass {
                item.indexPath = indexPath
                return cellView(with: cls, tableView: tableView, cellItem: item)
            }
        }
        fatalError()
    }
    
    public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        if let rowActions = cellItem(at: indexPath)?.rowActions {
            return rowActions.count > 0
        }
        return false
    }
    
    public func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return sectionIndexTitles
    }
    
    public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
        if let index = sectionForSectionIndexTitle?.firstIndex(of: index) {
            return index
        }
        return -1
    }
}
